package com.example.wificov;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.example.database.Point;
import com.example.database.myDBHelper;
import com.example.wificovv2.R;
import com.example.wificovv2.SeekAp;
import android.app.Activity;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Resources;
import android.database.sqlite.SQLiteDatabase;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.net.wifi.WifiManager;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.format.Formatter;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.ToggleButton;
import android.view.Menu;

public class Samplingbandpic extends Activity {
	private static final int APID = 0;

	myDBHelper db;
	
	String txtFileName = "iperf.txt";
	String ipserver; 
	float ratio1, ratio2;
	int imgwidth, imgheight;
	float x, y;
	
	Button  exitapp, collect;
	TextView output, interval, transfer, bandwidth;
	Resources res;
	ImageView picture;
	Bitmap mutableBitmap;
	
	public Samplingbandpic() {
	}
	//A global pointer for instances of of iperf (only one at a time is allowed). 
	IperfTask iperfTask = null;
	//This is a required method that implements the activity startup
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		//Shows the logo screen, and waits for a tap to continue to the main screen
		setContentView(R.layout.activity_samplingband);
		
		output = (TextView) findViewById(R.id.OutputText);
		interval = (TextView) findViewById(R.id.interval);
		transfer = (TextView) findViewById(R.id.transfer);
		bandwidth = (TextView) findViewById(R.id.bandwidth);
		collect= (Button)findViewById(R.id.collectpic);
		exitapp= (Button)findViewById(R.id.finishbwpic);
		picture = (ImageView) findViewById(R.id.picpow);
		
		ratio1 = 0f;
        ratio2 = 0f;               		
	    res = getResources();
	    int APID =Integer.parseInt(Samplingbandpic.this.getIntent().getStringExtra("apName"));
	    ipserver= db.getIP(APID);
	    
	    Paint p = new Paint();
	    p.setAntiAlias(true);	
	  //Get image's size
	    BitmapFactory.Options dimensions = new BitmapFactory.Options(); 
	    dimensions.inJustDecodeBounds = true;
	    Bitmap mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.newmap, dimensions);
	    imgheight = dimensions.outHeight;
	    imgwidth =  dimensions.outWidth;
	    
	    //calculate the ratios between image's size and real world distance - how many pixels per meter
//		    ratio1 = (float)imgwidth/160;
//		    ratio2 = (float)imgheight/100;
	    
	    //cast from immutableBitmap to mutableBitmap to insert into Canvas 
	    Bitmap bm = BitmapFactory.decodeResource( getResources(), R.drawable.newmap );
	    mutableBitmap = bm.copy(Bitmap.Config.ARGB_8888, true);
	    Canvas c = new Canvas( mutableBitmap );
	    // Vẽ lại tập điểm lên picture
	    Point point;
	    List<Point> list = db.getPoint(APID);	    
	    for(int i=1; i< list.size(); i++){
	    	point = list.get(i);	
	    	 x = point.x;
	    	 y = point.y;
	    	 p.setColor(Color.YELLOW);
	    	c.drawCircle(x*ratio1,y*ratio2 , 3, p);
	    }
	    //Tô màu những điểm đã lấy dữ liệu
	    
	    picture.setImageBitmap( mutableBitmap );
		    
		//Get Wifi connection information and displays it in the main screen
		getCurrentIP();	
		collect.setOnClickListener(new View.OnClickListener() {		
			@Override
			public void onClick(View v) {			
			Intent intent = new Intent(Samplingbandpic.this,SamplingPowPic.class);
			startActivity(intent);}});
		
		
		exitapp.setOnClickListener(new View.OnClickListener() {		
			@Override
			public void onClick(View v) { 
			Intent intent = new Intent(Samplingbandpic.this,ShowPowpicture.class);
			startActivity(intent);
			}});}	

		//Used to retrieve WiFi connection information and check if a connection exists. If not, display an error on the main screen, otherwise displays the local IP.
		public void getCurrentIP() {
			final TextView ip = (TextView) findViewById(R.id.ip);
			//An instance of WifiManger is used to retrieve connection info.
			WifiManager wim = (WifiManager) getSystemService(WIFI_SERVICE);
			if (wim.getConnectionInfo() != null) {
				if ((wim.getConnectionInfo().getIpAddress()) != 0) {
					//IP is parsed into readable format
					ip.append("Your IP address is: "
							+ Formatter.formatIpAddress(wim.getConnectionInfo()
									.getIpAddress()));
				} else {
					ip.append("Error: a wifi connection cannot be detected.");
				}
			} else {
				ip.append("Error: a wifi connection cannot be detected.");
			}
		}


		//This function is used to copy the iperf executable to a directory which execute permissions for this application, and then gives it execute permissions.
		//It runs on every initiation of an iperf test, but copies the file only if it's needed.
		public void initIperf() {
			final TextView tv = (TextView) findViewById(R.id.OutputText);
			InputStream in;
			try {
				//The asset "iperf" (from assets folder) inside the activity is opened for reading.
				in = getResources().getAssets().open("iperf");
			} catch (IOException e2) {
				tv.append("\nError occurred while accessing system resources, please reboot and try again.");
				return;			
			}
			try {
				//Checks if the file already exists, if not copies it.
				new FileInputStream("/data/data/com.example.wificovv2/iperf");
			} catch (FileNotFoundException e1) {
				try {
					//The file named "iperf" is created in a system designated folder for this application.
					OutputStream out = new FileOutputStream("/data/data/com.example.wificovv2/iperf", false); 
					// Transfer bytes from "in" to "out"
					byte[] buf = new byte[1024];
					int len;
					while ((len = in.read(buf)) > 0) {
						out.write(buf, 0, len);
					}
					in.close();
					out.close();
					//After the copy operation is finished, we give execute permissions to the "iperf" executable using shell commands.
					Process processChmod = Runtime.getRuntime().exec("/system/bin/chmod 744 /data/data/com.example.wificovv2/iperf"); 
					// Executes the command and waits untill it finishes.
					processChmod.waitFor();
				} catch (IOException e) {
					tv.append("\nError occurred while accessing system resources, please reboot and try again.");
					return;
				} catch (InterruptedException e) {
					tv.append("\nError occurred while accessing system resources, please reboot and try again.");
					return;
				}	
			}
			//Creates an instance of the class IperfTask for running an iperf test, then executes.
			iperfTask = new IperfTask();
			iperfTask.strServerIP = "192.168.8.103";
			iperfTask.execute();
			return;
		}

		//This method is used to handle toggle button clicks
		public void ToggleButtonClick(View v) {
			final ToggleButton toggleButton = (ToggleButton) findViewById(R.id.toggle);
			final EditText inputCommands = (EditText) findViewById(R.id.InputCommandspic);
			inputCommands.setText("-c "+ ipserver);		
			//If the button is not pushed (waiting for starting a test), then a iperf task is started.
			if (toggleButton.isChecked()) {
				InputMethodManager mgr = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
				mgr.hideSoftInputFromWindow(inputCommands.getWindowToken(), 0);
				initIperf();
			//If a test is already running then a cancel command is issued through the iperfTask interface.
			} else {
				if (iperfTask == null){
					toggleButton.setChecked(false);
					return;
				}
				iperfTask.cancel(true);
				iperfTask = null;
			}
		}
	
		//This method is used to handle the save button click
		public void SaveButtonClick(View v) {
			
			float inter = Float.parseFloat(interval.getText()+"");
			float tran = Float.parseFloat(transfer.getText()+"");
			float bw =Integer.parseInt(bandwidth.getText()+"");		
			//Luu vao database		
			db.insertBW(APID, inter , tran, bw);
		}	
		
	//The main class for executing iperf instances.
	//With every test started, an instance of this class is created, and is destroyed when the test is done.
	//This class extends the class AsyncTask which is used to perform long background tasks and allow updates to the GUI while running.
	//This is done by overriding certain functions that offer this functionality.
		public class IperfTask extends AsyncTask<Void, String, String> {
			final TextView tv = (TextView) findViewById(R.id.OutputText);
			final TextView tvInterval = (TextView) findViewById(R.id.interval);
			final TextView tvTransfer = (TextView) findViewById(R.id.transfer);
			final TextView tvBandwidth = (TextView) findViewById(R.id.bandwidth);
			
			final ScrollView scroller = (ScrollView) findViewById(R.id.Scroller);
			final EditText inputCommands = (EditText) findViewById(R.id.InputCommandspic);
			final ToggleButton toggleButton = (ToggleButton) findViewById(R.id.toggle);
			String txtOutput = new String();
			String strServerIP = "0.0.0.0";
	
			Process process = null;
	
			//This function is used to implement the main task that runs on the background.
			@Override
			protected String doInBackground(Void... voids) {
				// Reset txtOutput
				txtOutput = "";
	
				//Iperf command syntax check using a Regular expression to protect the system from user exploitation.
				strServerIP = inputCommands.getText().toString();
				String str = "-c " + strServerIP;
				if (!str.matches("(iperf )?((-[s,-server])|(-[c,-client] ([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5]))|(-[c,-client] \\w{1,63})|(-[h,-help]))(( -[f,-format] [bBkKmMgG])|(\\s)|( -[l,-len] \\d{1,5}[KM])|( -[B,-bind] \\w{1,63})|( -[r,-tradeoff])|( -[v,-version])|( -[N,-nodelay])|( -[T,-ttl] \\d{1,8})|( -[U,-single_udp])|( -[d,-dualtest])|( -[w,-window] \\d{1,5}[KM])|( -[n,-num] \\d{1,10}[KM])|( -[p,-port] \\d{1,5})|( -[L,-listenport] \\d{1,5})|( -[t,-time] \\d{1,8})|( -[i,-interval] \\d{1,4})|( -[u,-udp])|( -[b,-bandwidth] \\d{1,20}[bBkKmMgG])|( -[m,-print_mss])|( -[P,-parallel] d{1,2})|( -[M,-mss] d{1,20}))*"))
				{
					publishProgress("Error: invalid syntax. Please try again.\n\n");
					return null;
				}
				try {
					//The user input for the parameters is parsed into a string list as required from the ProcessBuilder Class.
					String[] commands = inputCommands.getText().toString().split(" ");
					List<String> commandList = new ArrayList<String>(Arrays.asList(commands));
					//If the first parameter is "iperf", it is removed
					if (commandList.get(0).equals((String)"iperf")) {
						commandList.remove(0);
					}
					//The execution command is added first in the list for the shell interface.
					commandList.add(0,"/data/data/com.example.wificovv2/iperf");
					//The process is now being run with the verified parameters.
					process = new ProcessBuilder().command(commandList).redirectErrorStream(true).start();
					//A buffered output of the stdout is being initialized so the iperf output could be displayed on the screen.
					BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
					int read;
					//The output text is accumulated into a string buffer and published to the GUI
					char[] buffer = new char[4096];
					StringBuffer output = new StringBuffer();
					while ((read = reader.read(buffer)) > 0) {
						output.append(buffer, 0, read);
						//This is used to pass the output to the thread running the GUI, since this is separate thread.
						publishProgress(output.toString());
						output.delete(0, output.length());
					}
					reader.close();
					process.destroy();
				}
				catch (IOException e) {
					publishProgress("\nError occurred while accessing system resources, please reboot and try again.");
					e.printStackTrace();
				}
				return null;
			}

		//This function is called by AsyncTask when publishProgress is called.
		//This function runs on the main GUI thread so it can publish changes to it, while not getting in the way of the main task.
		@Override
		public void onProgressUpdate(String... strings) {
			tv.append(strings[0]);
			txtOutput += strings[0];
			//The next command is used to roll the text to the bottom
//			scroller.post(new Runnable() {
//				public void run() {
//					scroller.smoothScrollTo(0, tv.getBottom());
//				}
//			});
		}

		//This function is called by the AsyncTask class when IperfTask.cancel is called.
		//It is used to terminate an already running task.
		@Override
		public void onCancelled() {
			//The running process is destroyed and system resources are freed.
			if (process != null) {
				process.destroy();
				try {
					process.waitFor();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			//The toggle button is switched to "off"
			toggleButton.setChecked(false);
			tv.append("\nOperation aborted.\n\n");
			//The next command is used to roll the text to the bottom
			scroller.post(new Runnable() {
				public void run() {
					scroller.smoothScrollTo(0, tv.getBottom());
				}
			});
		}

		@Override
		public void onPostExecute(String result) {
			//The running process is destroyed and system resources are freed.
			if (process != null) {
				process.destroy();
			
				try {
					process.waitFor();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				tv.append("\nTest is done.\n\n");
				
				//TODO: Parse text here
				Pattern p = Pattern.compile("\\S-(\\S+)\\s+(sec)\\s+(\\S+)\\s+(\\S+Bytes)\\s+(\\S+)\\s+(\\S+bits/sec)", Pattern.MULTILINE);

				//Pattern p = Pattern.compile("(---)");
				Matcher m = p.matcher(txtOutput);
				boolean bMatch = false;
				while (m.find()) { // Find each match in turn; String can't do this.
					bMatch = true;
					 float fTransferInKBytes = transferToKBytes(m.group(3), m.group(4));
					 float fBandWidthInKbits = bandwidthToKbits(m.group(5), m.group(6));
				     String name;// = m.group(1) + " " + m.group(2); // Access a submatch group; String can't do this.
				     name = "Interval = " + m.group(1) + "\n";
				     tv.append(name);
				     name = "Transfer = " + m.group(3) +  " - " + m.group(4) + "\n";
				     tv.append(name);
				     name = "Bandwidth = " + m.group(5) + " - " + m.group(6) + "\n";
				     tv.append(name);
				     name = "info = " + Float.toString(fTransferInKBytes) + " -- " + Float.toString(fBandWidthInKbits);
				     tv.append(name);
				     tvInterval.setText(m.group(1));
				     tvTransfer.setText(Float.toString(fTransferInKBytes));
				     tvBandwidth.setText(Float.toString(fBandWidthInKbits));
				 }
				
				if (!bMatch){
					tv.append("Error found");
				}

			}
			//The toggle button is switched to "off"
			toggleButton.setChecked(false);
			//The next command is used to roll the text to the bottom
//			scroller.post(new Runnable() {
//				public void run() {
//					scroller.smoothScrollTo(0, tv.getBottom());
//				}
//			});
		}
		
		private float transferToKBytes(String value, String unit)
		{
			float fValue = Float.parseFloat(value);
			float fFactor = 1;
			if (unit.equalsIgnoreCase("MBytes"))
			{
				fFactor = 1024;
			}
			else if (unit.equalsIgnoreCase("KBytes"))
			{
				fFactor = 1;
			}
			
			fValue = fValue * fFactor;
		
			return fValue;
		}
		
		private float bandwidthToKbits(String value, String unit)
		{
			float fValue = Float.parseFloat(value);
			float fFactor = 1;
			if (unit.equalsIgnoreCase("Mbits/sec"))
			{
				fFactor = 1024;
			}
			else if (unit.equalsIgnoreCase("Kbits/sec"))
			{
				fFactor = 1;
			}
			
			fValue = fValue * fFactor;
		
			return fValue;
		}		
	}

}
